#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>

#if defined STRF_OVERRIDE
char *f_strncpy(char *dest, const char *src, unsigned int n)
{
   unsigned int i;

   for (i = 0; i < n && src[i] != '\0'; i++)
	   dest[i] = src[i];
   for ( ; i < n; i++)
	   dest[i] = '\0';

   return dest;
}

char *f_strncat(char *dest, const char *src, unsigned int n)
{
   unsigned int dest_len = strlen(dest), i;

   for (i = 0 ; i < n && src[i] != '\0' ; i++)
	   dest[dest_len + i] = src[i];
   dest[dest_len + i] = '\0';

   return dest;
}

unsigned int f_strcspn(const char *s1, const char *s2)
{
	const char *p, *spanp;
	char c, sc;

	for (p = s1; ;) {
		c = *p++;
		spanp = s2;
		do {
			if ((sc = *spanp++) == c)
				return (p - 1 - s1);
		} while (sc != 0);
	}
	/* NOTREACHED */
}

#define isdigit(c) ((unsigned) ((c) - '0') < 10U)
int f_sscanf(const char *str, const char *format, ...)
{
	int result = 0, alen = 0;
	va_list args;
	
	va_start(args, format);
	for ( ; *format != '\0'; format++) {
		if (*format == '%') {
			if (isdigit(format[1])) {
				alen = 0;
				do {
					alen = (alen * 10) + (format[1] - '0');
					format++;
				} while (isdigit(format[1]));
			}
			if (format[1] == 'd') {
				int positive;
				int value;
				int *valp;
				if (*str == '-') {
					positive = 0;
					str++;
				} else
					positive = 1;
				if (!isdigit(*str))
					break;
				value = 0;
				do {
					value = (value * 10) - (*str - '0');
					str++;
				} while (isdigit(*str));
				if (positive)
					value = -value;
				valp = va_arg(args, int *);
				*valp = value;
				result++;
				format++;
			} else if (format[1] == 'u') {
				unsigned int *valp = va_arg(args, unsigned int *);
				unsigned int value = 0;
				if (!isdigit(*str))
					break;
				value = 0;
				do {
					value = (value * 10) + (*str - '0');
					str++;
				} while (isdigit(*str));
				*valp = value;
				result++;
				format++;
			} else if (format[1] == 'c') {
				char *valp = va_arg(args, char *);
				*valp = *str++;
				result++;
				format++;
			} else if (format[1] == 's') {
				char *valp = va_arg(args, char *);
				if (alen) {
					f_strncpy(valp, str, alen);
					str += alen;
				} else {
					/* UNDONE: */
					break;
				}
				result++;
				format++;
			}
		} else if (*format == *str) {
			str++;
		} else
			break;
	}
	va_end(args);
	return result;
}
#else /* STRF_OVERRIDE */
#define f_strcpy	strncpy
#define f_strncat	strncat
#define f_strcspn	strcspn
#define f_sscanf	sscanf
#endif /* STRF_OVERRIDE */

void get_next_arg(char **ks, char **ke, char **ve)
{
	while (*ks && **ks == ' ') (*ks)++;

	for (*ke = *ks; *ke && **ke && **ke != '=' && **ke != ' ' && **ke != '"'; (*ke)++);

	for (*ve = *ke; *ve && **ve && **ve != ' ' && **ve != '"'; (*ve)++);

	if (*ve && **ve == '"') for (*ve += 1; *ve && **ve && **ve != '"'; (*ve)++);

	if (*ve && **ve == '"') (*ve)++;
}


void splice_args(char *src1, char *src2, char *out, int replace_f)
{
	char *bp, *ep, *sp, *rp, *re, *rs;
	unsigned int rmask[4] = {0, 0, 0, 0}, count;

	for (bp = src1; bp && *bp; bp = sp) {
		get_next_arg(&bp, &ep, &sp);

		for (rp = src2, count = 0; rp && *rp; rp = rs, count++) {
			get_next_arg(&rp, &re, &rs);

			if ((rmask[(count/32) % 4] & (1 << (count % 32))) == 0 &&
				ep - bp == re - rp && strncmp(bp, rp, ep - bp) == 0) {
				if (replace_f) f_strncat(out, rp, rs - rp);
				rmask[(count/32) % 4] |= (1 << (count % 32));
				break;
			}
		}

		if (!rp || !*rp) f_strncat(out, bp, sp - bp);

		strcat(out, " ");
	}
}


#define TEXT_CHUNK_SIZE		16384
int main(int argc, char *argv[])
{
	static char defparams[] = "console=ttyS0,115200 quiet mtdparts=atmel_nand:256k(boot)ro,256k(original_env)ro,256k(runtime_env),2M(bootloader_env),5M(kernel1),95M(rootfs1),5M(kernel2),95M(rootfs2),100M(service),-(userdata) ro rootfstype=ubifs rootflags=sync ubi.fm_autoconvert=1 ubi.mtd=7,0,2 ubi.mtd=9 root=ubi0:rootfs", mtdhead[] = "mtdparts=atmel_nand:", kernhead[] = "kernel";

	const unsigned int div1K = 1024;
	const unsigned int div1M = 1048576;
	const unsigned int div1G = 1073741824;

	char *result, *input, *ep, sc, label[32];
	int slen, clen;
	unsigned int size, offs;

	if (argc < 2) {
		fprintf(stderr, "usage: %s <parameters override>\n", argv[0]);
		return (EINVAL);
	}

	result =(char *)malloc(TEXT_CHUNK_SIZE);
	if (!result) {
		fprintf(stderr, "memory allocation failed\n");
		return (ENOMEM);
	}

	result[0] = 0;

	input =(char *)malloc(TEXT_CHUNK_SIZE);
	if (!input) {
		fprintf(stderr, "memory allocation failed\n");
		free(result);
		return (ENOMEM);
	}

	input[0] = 0;

	for (int i = 1; i < argc; i++) {
		strcat(input, argv[i]);
		strcat(input, " ");
	}

	/*----- merge kernel arguments -----*/
	splice_args(defparams, input, result, 1);

	splice_args(input, defparams, result, 0);

	fprintf(stdout, "%s\n", result);

	/*----- get kernel image addr -----*/
	ep = strstr(result, mtdhead);
	if (ep) {
		for (ep += (sizeof (mtdhead) - 1), offs = 0; ep && *ep; offs += size) {

			if (f_sscanf(ep, "%u%c(%31s),", &size, &sc, label) == 3) {
				switch (sc) {
				case 'k':
				case 'K': size = size * div1K; break;
				case 'm':
				case 'M': size = size * div1M; break;
				case 'g':
				case 'G': size = size * div1G; break;
				default: break;
				}
				label[f_strcspn(label, ")")] = 0;
			} else {
				break;
			}

			if (strncmp(label, kernhead, sizeof (kernhead) - 1) == 0)
				fprintf(stdout, "  %10s  0x%x\n", label, offs);

			slen = strlen(ep);
			clen = f_strcspn(ep, ",") + 1;
			ep += (slen > clen ? clen : slen);
		}
	}

	free(input);
	free(result);

	return (0);
}
